/***************************************************************************
 *   Copyright (C) 2008 by BogDan Vatra                                    *
 *   bogdan@licentia.eu                                                    *
 *                                                                         *
 *   This program is free software: you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation, either version 3 of the License, or     *
 *   (at your option) any later version.                                   *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
 ***************************************************************************/
#ifndef SQLQUERY_H
#define SQLQUERY_H

#include <QObject>
#include <QSqlQuery>
#include <QSqlField>
#include <QSqlRecord>
#include <QStringList>
#include <QItemSelectionModel>

#include "database.h"
#include "querymodel.h"
#include "win32def.h"

namespace SQL
{

/**
	@author BogDan Vatra <taipan@omnidatagrup.ro>
*/
class KONTAMABIL_EXPORTS Query : public QObject
{
	Q_OBJECT
public:
	enum QueryState{Invalid, Select, Insert, Update, Delete};
	enum LocateOption{NoOption=0, CaseSensitive=1, PartialSearch=2, SortedSearch=4};
	Q_FLAGS(LocateOptions LocateOption)
	Q_DECLARE_FLAGS(LocateOptions, LocateOption)

public:
	Query(QObject *parent = defaultDatabase(),  const QString & query = QString(), QSqlDatabase db = defaultDatabase()->database());
	~Query();

	Q_INVOKABLE void setSelectQuery(const QString & sql);
	Q_INVOKABLE void setInsertQuery(const QString & sql);
	Q_INVOKABLE void setUpdateQuery(const QString & sql);
	Q_INVOKABLE void setRemoveQuery(const QString & sql);

	Q_INVOKABLE void setRefreshQuery(const QString & sql);

	Q_INVOKABLE void setPrimaryKey(const QString & primaryKey);

	Q_INVOKABLE void setListenNotifyMessage(const QString & message);

	Q_INVOKABLE void setPrivilegesForTable(const QString & table);

	Q_INVOKABLE bool canSelect();
	Q_INVOKABLE bool canInsert();
	Q_INVOKABLE bool canUpdate();
	Q_INVOKABLE bool canDelete();

	Q_INVOKABLE void setOrderFields(const QStringList & orderFields);
	Q_INVOKABLE const QStringList & orderFields();

	Q_INVOKABLE int at();
	Q_INVOKABLE QSqlRecord record();
	Q_INVOKABLE const QList<QSqlRecord> & result();
	Q_INVOKABLE int size();

	Q_INVOKABLE bool isActive() const;
	Q_INVOKABLE bool isValid() const;

	Q_INVOKABLE QueryState state();

	Q_INVOKABLE bool locate(const QString & fieldName, const QVariant & value, LocateOptions locateOptions=NoOption);
	Q_INVOKABLE bool locate(const QString & fieldName, const QVariant & value, bool caseSensitive , bool partial , bool sortedSearch);
	Q_INVOKABLE bool locate(int fieldNumber, const QVariant & value, bool caseSensitive, bool partial , bool sortedSearch);

	Q_INVOKABLE QVariant value(const QString & fieldName);
	Q_INVOKABLE QVariant value(int fieldNumber);

	Q_INVOKABLE void setValue(QString fieldName, const QVariant & value);
	Q_INVOKABLE void setValue(int fieldNumber, const QVariant & value);

	Q_INVOKABLE void bindValue(QString placeholder, const QVariant & val);
	Q_INVOKABLE void bindValue(int pos, const QVariant & val);

	Q_INVOKABLE QVariant boundValue(QString placeholder) const;
	Q_INVOKABLE QVariant boundValue(int pos) const;

	QueryModel * createModel(const QStringList & columns);
	QItemSelectionModel * createSelectionModel(QAbstractItemModel * model);

	static	bool typeIsNumber(QVariant::Type type)
	{
		if (QVariant::Int == type || QVariant::LongLong == type || QVariant::UInt == type || QVariant::ULongLong == type || QVariant::Double == type)
			return true;
		return false;
	}


public slots:
	void select();
	void insert();
	void update();
	void remove();
	void cancel();
	void post();
	bool first();
	bool last();
	bool next();
	bool previous();
	bool seek(int pos);
	void seek(const QModelIndex&, const QModelIndex&);
	void notification(const QString & name );
	void close(bool clearBoundValue=false);

signals:
	void cursorFirst(const QSqlRecord & record);
	void cursorNext(const QSqlRecord & record);

	void cursorPrevious(const QSqlRecord & record);
	void cursorLast(const QSqlRecord & record);

	void cursorSeek(int pos);

	void recordChanged(const QSqlRecord & record);
	void valueChanged(const QSqlField & field);

	void beforeSelect();
	void afterSelect();

	void beforeInsert();
	void afterInsert(int pos);

	void beforeUpdate();
	void afterUpdate(int pos);

	void beforeRemove();
	void afterRemove(int pos);

	void afterPost();
	void afterCancel();

	void beforePost();
	void afterClose();

	void stateChanged(Query::QueryState state);

protected:
	void setQueryState(QueryState state);

private:
	int cmpVariant(const QVariant & val1, const QVariant & val2, bool caseSensitive);
	int cmpRecords(QSqlRecord rec1, QSqlRecord rec2);
	void addRecord(const QSqlRecord & record);
	void bindValuesToQuery(QSqlQuery * qry);

private:
	QStringList m_refreshPlaceholders;
	QStringList m_iudPlaceholders;
	QStringList m_selectPlaceholders;
	QSqlQuery * m_selectQuery;
	QSqlQuery * m_iudQuery;
	int m_currentPos;
	QueryState m_queryState;
	QString m_selectSql, m_insertSql, m_updateSql, m_removeSql, m_refreshSql;
	bool m_refreshQuery;
	QSqlDatabase m_db;
	QList<QSqlRecord> m_result;
	QStringList m_orderFields;
	QString m_listenNotify,m_primaryKey, m_privilegesForTable;
	QMap<QString, QVariant> m_stringBindValues;
	QMap<int, QVariant> m_intBindValues;
	bool m_on_event;
};

}

#endif
